#include "StdAfx.h"
#include "DriverD3D.h"
#include "I3DEngine.h"
#include "D3DPostProcess.h"

#pragma warning(disable: 4244)

#if defined(DIRECT3D10) && !defined(PS3)
static const uint32 RAIN_OCC_MAP_SIZE = 512;
#else
static const uint32 RAIN_OCC_MAP_SIZE = 256;
#endif

bool CD3D9Renderer::FX_DeferredCaustics( ) 
{ 
	if( !CRenderer::CV_r_watercaustics || !CRenderer::CV_r_watercausticsdeferred || !CTexture::s_ptexBackBuffer || !CTexture::s_ptexSceneTarget )
		return false;

	const Vec4 causticParams = gEnv->p3DEngine->GetOceanAnimationCausticsParams();
	float fCausticsHeight = causticParams.y;
	float fCausticsDepth = causticParams.z;
	float fCausticsIntensity = causticParams.w;

	N3DEngineCommon::SOceanInfo &OceanInfo= gRenDev->m_p3DEngineCommon.m_OceanInfo;
	bool bOceanVolumeVisible = (OceanInfo.m_nOceanRenderFlags & OCR_OCEANVOLUME_VISIBLE) != 0;
	if( !bOceanVolumeVisible || iszero(fCausticsIntensity))
		return false;

	uint64 nFlagsShaderRTSave = gcpRendD3D->m_RP.m_FlagsShader_RT;

	if (m_LogFile)
		Logv(SRendItem::m_RecurseLevel[m_RP.m_nProcessThreadID], " +++ Deferred caustics pass begin +++ \n");

	GPU_TIMER_START("CAUSTICS")
	PROFILE_LABEL_PUSH("DEFERRED WATER CAUSTICS");
	PROFILE_FRAME(DrawShader_DeferredCausticsPass);

	float fWatLevel = OceanInfo.m_fWaterLevel;
	float fCausticsLevel = fWatLevel+fCausticsHeight;
	Vec4 pCausticsParams = Vec4(CRenderer::CV_r_watercausticsdistance, OceanInfo.m_vCausticsParams.z * fCausticsIntensity,
																																		OceanInfo.m_vCausticsParams.w, fCausticsLevel) ;
	static float fDist = 0.0f;
	
	fDist = sqrtf((pCausticsParams.x * 5.0f) * 13.333f);  // Hard cut off when caustic would be attenuated to 0.2 (1/5.0f)

	I3DEngine *pEng = gEnv->p3DEngine;  

	// Caustics are done with projection from sun - hence they update too fast with regular
	// sun direction. Use a smooth sun direction update instead to workaround this

	SCGParamsPF &PF = gRenDev->m_cEF.m_PF;
	{
		Vec3 pRealtimeSunDirNormalized = pEng->GetRealtimeSunDirNormalized();

		const float fSnapDot = 0.98f; 
		float fDot = fabs(PF.vCausticsCurrSunDir.Dot(pRealtimeSunDirNormalized));
		if( fDot < fSnapDot )
			PF.vCausticsCurrSunDir = pRealtimeSunDirNormalized;   

		PF.vCausticsCurrSunDir += (pRealtimeSunDirNormalized - PF.vCausticsCurrSunDir) * 0.005f * gEnv->pTimer->GetFrameTime();
		PF.vCausticsCurrSunDir.Normalize(); 
	}

	Matrix44 m_pLightView;

	Vec3 up = Vec3(0,0,1);
	Vec3 dirZ = -PF.vCausticsCurrSunDir.GetNormalized();
	Vec3 dirX = up.Cross(dirZ).GetNormalized();
	Vec3 dirY = dirZ.Cross(dirX).GetNormalized();

	m_pLightView.SetIdentity();
	m_pLightView.SetRow(0, dirX);
	m_pLightView.SetRow(1, dirY);
	m_pLightView.SetRow(2, dirZ);

	float fTime = 0.125f * gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_RealTime;

	Vec4 vAnimParams = Vec4(0.06f * fTime, 0.05f * fTime, 0.1f * fTime, -0.11f * fTime);

	// Stencil pre-pass
	if(  CRenderer::CV_r_watercausticsdeferred == 2 ) 
	{
		// stencil pre-pass
		CShader *pSH( CShaderMan::m_ShaderShadowMaskGen );
		//EF_ClearBuffers(FRT_CLEAR_STENCIL|FRT_CLEAR_IMMEDIATE, NULL, 1); 

		// make box for stencil passes
		t_arrDeferredMeshIndBuff arrDeferredInds;
		t_arrDeferredMeshVertBuff arrDeferredVerts;
		CreateDeferredUnitBox(arrDeferredInds, arrDeferredVerts);

		Vec3 vCamPos = gRenDev->GetRCamera().Orig;
		float fWaterPlaneSize = gRenDev->GetCamera().GetFarPlane();

		m_RP.m_TI[m_RP.m_nProcessThreadID].m_matView->Push();
		Matrix34 mLocal;
		mLocal.SetIdentity();

		float heightAboveWater  = max(0.0f, vCamPos.z - fCausticsLevel);
		fDist = sqrtf(max((fDist*fDist) - (heightAboveWater*heightAboveWater), 0.0f));

		//TODO: Adjust Z on fog density

		mLocal.SetScale(Vec3(fDist*2, fDist*2, fCausticsHeight + fCausticsDepth));
		mLocal.SetTranslation( Vec3(vCamPos.x-fDist, vCamPos.y-fDist, fWatLevel - fCausticsDepth) );
		
		Matrix44 mLocalTransposed = GetTransposed44(Matrix44(mLocal));
		m_RP.m_TI[m_RP.m_nProcessThreadID].m_matView->MultMatrixLocal(&mLocalTransposed);

		uint32 nPasses = 0;         
		static CCryNameTSCRC TechName0 = "DeferredShadowPass";
		pSH->FXSetTechnique(TechName0);
		pSH->FXBegin( &nPasses, FEF_DONTSETSTATES );
		pSH->FXBeginPass( DS_SHADOW_CULL_PASS );

		uint32 nVertOffs, nIndOffs;

		//allocate vertices
		SVF_P3F_C4B_T2F *pVerts = (SVF_P3F_C4B_T2F *)m_DynVB[POOL_P3F_COL4UB_TEX2F].LockVB(arrDeferredVerts.size(), nVertOffs);
		memcpy(pVerts, &arrDeferredVerts[0], arrDeferredVerts.size()*sizeof(SVF_P3F_C4B_T2F));
		m_DynVB[POOL_P3F_COL4UB_TEX2F].UnlockVB();

		//allocate indices
		uint16 *pInds = m_DynIB.LockIB(arrDeferredInds.size(), nIndOffs);
		memcpy( pInds, &arrDeferredInds[0], sizeof(uint16)*arrDeferredInds.size());
		m_DynIB.UnlockIB();

		m_DynVB[POOL_P3F_COL4UB_TEX2F].Bind(0, 0);
		m_DynIB.Bind();

		if (!FAILED(FX_SetVertexDeclaration( 0, eVF_P3F_C4B_T2F )))
			FX_StencilCullPass(-1, nVertOffs, arrDeferredVerts.size(), nIndOffs, arrDeferredInds.size());

		pSH->FXEndPass();
		pSH->FXEnd();

		m_RP.m_TI[m_RP.m_nProcessThreadID].m_matView->Pop();

		FX_StencilTestCurRef(true, false);
	
	}

	// Deferred caustic pass
	gcpRendD3D->EF_Scissor(false, 0, 0, 0, 0);

	gRenDev->m_cEF.mfRefreshSystemShader("DeferredCaustics", CShaderMan::m_ShaderDeferredCaustics);

	CShader *pShader = CShaderMan::m_ShaderDeferredCaustics;
	gcpRendD3D->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]|g_HWSR_MaskBit[HWSR_SAMPLE3];

	static CCryNameTSCRC pTechName = "General";
	SD3DPostEffectsUtils::ShBeginPass(pShader, pTechName, FEF_DONTSETSTATES);

	int32 nRState = GS_NODEPTHTEST |(( CRenderer::CV_r_watercausticsdeferred == 2)?GS_STENCIL:0)|(GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCALPHA);

	gcpRendD3D->FX_SetState(nRState);      

	static CCryNameR m_pParamAnimParams("vAnimParams");
	static CCryNameR m_pCausticParams("vCausticParams");
	static CCryNameR m_pParamLightView("mLightView");
	pShader->FXSetPSFloat(m_pParamAnimParams,  &vAnimParams, 1);
	pShader->FXSetPSFloat(m_pCausticParams,  &pCausticsParams, 1);
	pShader->FXSetPSFloat(m_pParamLightView, (Vec4*) m_pLightView.GetData(), 4);

	SD3DPostEffectsUtils::DrawFullScreenQuadWPOS(CTexture::s_ptexSceneTarget->GetWidth(), CTexture::s_ptexSceneTarget->GetHeight()); // TODO: Use Volume

	SD3DPostEffectsUtils::ShEndPass();

	if(  CRenderer::CV_r_watercausticsdeferred == 2 ) 
		FX_StencilTestCurRef(false);

	if (m_LogFile)
		Logv(SRendItem::m_RecurseLevel[m_RP.m_nProcessThreadID], " +++ Deferred caustics pass end +++ \n");

	gcpRendD3D->m_RP.m_FlagsShader_RT = nFlagsShaderRTSave;
	//m_RP.m_TI[m_RP.m_nProcessThreadID].m_PersFlags2 = nPersFlags2Save;

	FX_ResetPipe();

	PROFILE_LABEL_POP("DEFERRED WATER CAUSTICS");
	GPU_TIMER_STOP("CAUSTICS")
	return true;
}

bool CD3D9Renderer::FX_DeferredRainOcclusionMap(const N3DEngineCommon::ArrOccluders & arrOccluders, const N3DEngineCommon::SRainInfo & rainVolParams)
{
	PROFILE_LABEL_PUSH("OCCLUSION_PASS");

	const Matrix44 & matOccTrans = rainVolParams.m_matOccTrans;
	uint64 nFlagsShaderRTSave = m_RP.m_FlagsShader_RT;

	Matrix44 matTrans;
	static const Matrix44 matSs2Ps
		( 2.f,  0.f,  0.f, -1.f,
		0.f,  2.f,  0.f, -1.f,
		0.f,  0.f,  1.f,  0.f,
		0.f,  0.f,  0.f,  1.f );
	matTrans.Multiply(matSs2Ps, matOccTrans);

	// Create texture if required
	if (!CTexture::IsTextureExist(CTexture::s_ptexRainOcclusion))
	{
		if (!CTexture::s_ptexRainOcclusion->Create2DTexture(RAIN_OCC_MAP_SIZE, RAIN_OCC_MAP_SIZE, 1,
			FT_DONT_RELEASE | FT_DONT_STREAM | FT_DONT_RESIZE | FT_USAGE_RENDERTARGET,
			0, eTF_A8R8G8B8, eTF_A8R8G8B8))
			return false;
	}
	CTexture::s_ptexRainOcclusion->SetRenderTargetTile(0);

	// Get temp depth buffer
	SD3DSurface * pTmpDepthSurface = FX_GetDepthSurface(RAIN_OCC_MAP_SIZE, RAIN_OCC_MAP_SIZE, false);

	// Render geometry to rain occlusion map
	FX_PushRenderTarget(0, CTexture::s_ptexRainOcclusion, pTmpDepthSurface);

	// Get current viewport
	int iTempX, iTempY, iWidth, iHeight;
	GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
	RT_SetViewport(0, 0, RAIN_OCC_MAP_SIZE, RAIN_OCC_MAP_SIZE);

	const ColorF clClear(1,1,1,1);
	EF_ClearBuffers(FRT_CLEAR_COLOR | FRT_CLEAR_DEPTH, &clClear);

	FX_SetState(GS_DEPTHFUNC_LEQUAL | GS_DEPTHWRITE);
	SetCullMode(R_CULL_NONE);

	CShader * pSH = m_cEF.m_ShaderDeferredRain;
	uint32 nPasses = 0;
	static CCryNameTSCRC TechName("RainOcclusion");
	pSH->FXSetTechnique(TechName);
	pSH->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
	pSH->FXBeginPass(0);

	static CCryNameR sParamName1("g_RainOcc_TransMat");
	for (N3DEngineCommon::ArrOccluders::const_iterator it = arrOccluders.begin();
		it != arrOccluders.end(); it++)
	{
		if (it->m_RndMesh)
		{
			Matrix44A matWVP(it->m_WorldMat);
			matWVP.Multiply(matTrans, matWVP);
			pSH->FXSetVSFloat(sParamName1, (Vec4*)matWVP.GetData(), 3);

			FX_Commit();

			((CRenderMesh *)it->m_RndMesh)->DrawImmediately();
		}
	}

	pSH->FXEndPass();
	pSH->FXEnd();

	FX_PopRenderTarget(0);

	PROFILE_LABEL_POP("OCCLUSION_PASS");

	RT_SetViewport(iTempX, iTempY, iWidth, iHeight);

	gcpRendD3D->m_RP.m_FlagsShader_RT = nFlagsShaderRTSave;

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool CD3D9Renderer::FX_DeferredRainOcclusion()
{
	N3DEngineCommon::SRainInfo & rainVolParams = m_p3DEngineCommon.m_RainInfo;

	if (rainVolParams.m_AreaAABB.IsReset())
		return false;

	if (m_p3DEngineCommon.m_RainOccluders.m_bProcessed[RT_GetCurrGpuID()])
		return true;

	PROFILE_LABEL_PUSH("DEFERRED_RAIN_OCCLUSION");

	bool bRet = true;
	const N3DEngineCommon::ArrOccluders & arrOccluders = m_p3DEngineCommon.m_RainOccluders.m_arrCurrOccluders[m_RP.m_nProcessThreadID];
	if (!arrOccluders.empty())
	{
		// Render occluders to occlusion map
		bRet = FX_DeferredRainOcclusionMap(arrOccluders, rainVolParams);
		m_p3DEngineCommon.m_RainOccluders.m_bProcessed[RT_GetCurrGpuID()] = true;
		if (bRet)
			rainVolParams.m_matOccTransRender = rainVolParams.m_matOccTrans;
	}

	PROFILE_LABEL_POP("DEFERRED_RAIN_OCCLUSION");

	return bRet;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool CD3D9Renderer::FX_DeferredRainPreprocess()
{
	// Snow also uses the occlusion computation.
	CEffectParam * pRainActive = PostEffectMgr()->GetByName("SceneRain_Active");
	if (pRainActive)
		pRainActive->SetParam(0);

	CEffectParam * pSnowActive = PostEffectMgr()->GetByName("SceneSnow_Active");
	if (pSnowActive)
		pSnowActive->SetParam(0);

	if (!CV_r_PostProcess
			|| !CTexture::s_ptexBackBuffer



			|| !CTexture::s_ptexSceneTarget

			)
	{
		if (CTexture::s_ptexRainOcclusion)
			CTexture::s_ptexRainOcclusion->hintRelease();
		return false;
	}

	N3DEngineCommon::SRainInfo & rainVolParams = m_p3DEngineCommon.m_RainInfo;
	N3DEngineCommon::SSnowInfo & snowVolParams = m_p3DEngineCommon.m_SnowInfo;

	bool bRenderSnow = ((snowVolParams.m_fSnowAmount > 0.05f || snowVolParams.m_fFrostAmount > 0.05f) && snowVolParams.m_fRadius > 0.05f && CV_r_snow > 0);
	bool bRenderRain = (rainVolParams.m_fAmount * CRenderer::CV_r_rainamount > 0.05f && rainVolParams.m_fRadius > 0.05f && CV_r_rain > 0);

	bool bRender = bRenderSnow || bRenderRain;
	if (!bRender)
		return false;

	bool bRet = true;
	if (rainVolParams.m_bApplyOcclusion && ((CV_r_snow == 2 && bRenderSnow) || (CV_r_rain == 2 && bRenderRain)))
	{
		bRet = FX_DeferredRainOcclusion();
		if (!bRet && CTexture::s_ptexRainOcclusion)
			CTexture::s_ptexRainOcclusion->hintRelease();
	}
	else if (CTexture::s_ptexRainOcclusion)
		CTexture::s_ptexRainOcclusion->hintRelease();

	if (bRet && pRainActive)
	{
		CSceneRain * pEffRain = (CSceneRain *)PostEffectMgr()->GetEffect(ePFX_SceneRain);
		if (!pEffRain)
			return false;

		pEffRain->m_RainVolParams = rainVolParams;
		pRainActive->SetParam(1);
	}

	if (bRet && pSnowActive)
	{
		CSceneSnow * pEffSnow = (CSceneSnow *)PostEffectMgr()->GetEffect(ePFX_SceneSnow);
		if (!pEffSnow)
			return false;

		pEffSnow->m_RainVolParams = rainVolParams;
		pEffSnow->m_SnowVolParams = snowVolParams;
		pSnowActive->SetParam(1);
	}

	return bRet;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool CD3D9Renderer::FX_DeferredRainReflection(SDynTexture * pRTReflection)
{
	if( !pRTReflection )
		return false;

	CTexture * pTexReflection = pRTReflection->m_pTexture;
	if( !pTexReflection )
		return false;

	pTexReflection->SetRenderTargetTile(1);

	PROFILE_LABEL_PUSH("VERTICAL_BLUR");
	// Blur screen vertically for faking screen space reflections
	{
		ETEX_Format fmt = pTexReflection->GetDstFormat();
		SDynTexture *tpBlurTemp = new SDynTexture( pRTReflection->GetWidth(), pRTReflection->GetHeight(), fmt, eTT_2D, FT_STATE_CLAMP, "TempSSReflRT" );
		tpBlurTemp->Update( pRTReflection->GetWidth(), pRTReflection->GetHeight() );

		if( !tpBlurTemp->m_pTexture )
		{
			SAFE_DELETE(tpBlurTemp);
			return false;
		}

		tpBlurTemp->m_pTexture->SetRenderTargetTile(1);

		gcpRendD3D->Set2DMode(true, 1, 1);     




		PostProcessUtils().StretchRect(CTexture::s_ptexSceneTarget, pTexReflection);


		uint64 nFlagsShaderRT = gcpRendD3D->m_RP.m_FlagsShader_RT;
		gcpRendD3D->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];

		float fScale = -32.0f;

		// Get current viewport
		int iTempX, iTempY, iWidth, iHeight;
		gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
		gcpRendD3D->RT_SetViewport(0, 0,  pRTReflection->GetWidth(), pRTReflection->GetHeight());        

		Vec4 vWhite( 1.0f, 1.0f, 1.0f, 1.0f );

		PROFILE_LABEL_PUSH("ANISOTROPIC_VERTICAL");

		CCryNameTSCRC pTechName("AnisotropicVertical");
		CShader *m_pCurrShader = CShaderMan::m_shPostEffects;

		uint32 nPasses;

		// setup texture offsets, for texture sampling
		float s1 = 1.0f/(float) pRTReflection->GetWidth();     
		float t1 = 1.0f/(float) pRTReflection->GetHeight();    

		Vec4 pWeightsPS;
		pWeightsPS.x = (1.0f / 8.0f)* t1;
		pWeightsPS.y = 0.5f * t1;
		pWeightsPS.z = 0.75f * t1;
		pWeightsPS.w = 1.0f * t1;

		pWeightsPS *= fScale;

		STexState sTexState = STexState(FILTER_LINEAR, true);
		sTexState.m_nAddressU = TADDR_BORDER;
		sTexState.m_nAddressV = TADDR_BORDER;
		sTexState.SetBorderColor(0);

		static CCryNameR pParam0Name("blurParams0");

		m_pCurrShader->FXSetTechnique(pTechName);
		m_pCurrShader->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
		m_pCurrShader->FXBeginPass(0);

    gRenDev->FX_SetState(GS_NODEPTHTEST);   

		CShaderMan::m_shPostEffects->FXSetVSFloat(pParam0Name, &pWeightsPS, 1);  
		gcpRendD3D->FX_PushRenderTarget(0, tpBlurTemp->m_pTexture, NULL);
		gcpRendD3D->RT_SetViewport(0, 0, pRTReflection->GetWidth(), pRTReflection->GetHeight());        

		pRTReflection->Apply(0, CTexture::GetTexState(sTexState)); 
		PostProcessUtils().DrawFullScreenQuad(pRTReflection->GetWidth(), pRTReflection->GetHeight());

		gcpRendD3D->FX_PopRenderTarget(0);
		m_pCurrShader->FXEndPass();
		m_pCurrShader->FXEnd(); 

		pWeightsPS*=0.5f;

		m_pCurrShader->FXSetTechnique(pTechName);
		m_pCurrShader->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
		m_pCurrShader->FXBeginPass(0);

		gcpRendD3D->FX_PushRenderTarget(0, pTexReflection, NULL);
		gcpRendD3D->RT_SetViewport(0, 0, pRTReflection->GetWidth(), pRTReflection->GetHeight());         

		CShaderMan::m_shPostEffects->FXSetVSFloat(pParam0Name, &pWeightsPS, 1);  
		tpBlurTemp->m_pTexture->Apply(0, CTexture::GetTexState(sTexState)); 

		PostProcessUtils().DrawFullScreenQuad( pRTReflection->GetWidth(), pRTReflection->GetHeight());      

		gcpRendD3D->FX_PopRenderTarget(0);

		m_pCurrShader->FXEndPass();
		m_pCurrShader->FXEnd(); 

		PROFILE_LABEL_POP("BLUR");

		gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);

		SAFE_DELETE(tpBlurTemp);

		m_RP.m_FlagsShader_RT = nFlagsShaderRT;

		gcpRendD3D->Set2DMode(false, 1, 1);     
	}

	PROFILE_LABEL_POP("VERTICAL_BLUR");

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool CD3D9Renderer::FX_DeferredRainLayer()
{
	const N3DEngineCommon::SRainInfo & rainVolParams = m_p3DEngineCommon.m_RainInfo;
	uint64 nFlagsShaderRTSave = m_RP.m_FlagsShader_RT;




	CTexture * pSceneTarget = CTexture::s_ptexSceneTarget;
	FX_ScreenStretchRect(CTexture::s_ptexSceneTarget);


	ETEX_Format fmt = pSceneTarget->GetDstFormat();
	SDynTexture *pTexScreenSpaceRefl = new SDynTexture( m_width / 4, m_height / 4, fmt, eTT_2D, FT_STATE_CLAMP, "SSReflRT" );
	pTexScreenSpaceRefl->Update( m_width / 4, m_height / 4 );
	
	if (!FX_DeferredRainReflection(pTexScreenSpaceRefl))
	{
		SAFE_DELETE(pTexScreenSpaceRefl);
		return false;
	}

  if (m_LogFile)
    Logv(SRendItem::m_RecurseLevel[m_RP.m_nProcessThreadID], " +++ Deferred rain pass begin +++ \n");

	PROFILE_LABEL_PUSH("COMPOSITE_PASS");
  {
		const CRenderCamera & cCam = gcpRendD3D->m_RP.m_TI[m_RP.m_nProcessThreadID].m_rcam;
		const Vec4 vDepthBounds = CDeferredShading::Instance().GetLightDepthBounds(rainVolParams.m_vWorldPos, rainVolParams.m_fRadius);
		const float fMinZ = CV_r_rain_ignore_nearest ? CV_r_DrawNearZRange : 0.f;
		float fMaxZ = -1.f;
		if (CV_r_rain_maxviewdist_deferred > cCam.Near)
			fMaxZ = (cCam.Far - (cCam.Near * cCam.Far) / CV_r_rain_maxviewdist_deferred) / (cCam.Far - cCam.Near);







































		gRenDev->m_cEF.mfRefreshSystemShader("DeferredRain", CShaderMan::m_ShaderDeferredRain);

		// Used for water puddle texture translation animation
		static CCryNameR paramPuddleName("g_RainPuddleParams");
		float fTime = m_RP.m_TI[m_RP.m_nProcessThreadID].m_RealTime * 0.333f;
		float fSpecAmount = rainVolParams.m_fFakeGlossiness;



		const Vec4 vPuddleParams = Vec4(sinf(fTime) * 0.25f, cosf(fTime) * 0.25f, rainVolParams.m_fPuddelsAmount * 0.5f, fSpecAmount);

		CShader *pShader = CShaderMan::m_ShaderDeferredRain;
    m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]|g_HWSR_MaskBit[HWSR_SAMPLE3];
		if (IsHDRModeEnabled())
			m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_HDR_MODE];
		if (rainVolParams.m_bApplyOcclusion)
			m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
		
		// Bind reflection texture to $FromRE0
		m_RP.m_RECustomTexBind[0] = pTexScreenSpaceRefl->m_pTexture->GetID();
		
		static CCryNameTSCRC pTechName = "General";
    SD3DPostEffectsUtils::ShBeginPass(pShader, pTechName, FEF_DONTSETSTATES);
    FX_SetState(GS_NODEPTHTEST);

    static CCryNameR paramName("g_RainVolumeParams");
		const Vec3 & vCamPos = cCam.Orig;
		const Vec4 vRainPosCS = Vec4(rainVolParams.m_vWorldPos, 1.f / max(rainVolParams.m_fRadius, 1e-3f));
    pShader->FXSetPSFloat(paramName, &vRainPosCS, 1);

    static CCryNameR param1Name("g_RainColorMultipliers");
		float fAmount = rainVolParams.m_fAmount * rainVolParams.m_fReflectionAmount * CV_r_rainamount;



		Vec4 vRainColorMultipliers = Vec4(rainVolParams.m_vColor, 1.f) * fAmount;
		vRainColorMultipliers.w = fMaxZ > 0 ? CV_r_rain_maxviewdist_deferred / cCam.Far : 1.f;
		vRainColorMultipliers.w = -10.f / vRainColorMultipliers.w;
		pShader->FXSetPSFloat(param1Name, &vRainColorMultipliers, 1);
    
    static CCryNameR param2Name("g_WorldViewPos");
    Vec4 pCamPosParam = Vec4(vCamPos, max(rainVolParams.m_fUmbrellaRadius, 0.f) * rainVolParams.m_fUmbrellaRadius - 1.f);
    pShader->FXSetPSFloat(param2Name, &pCamPosParam, 1);
		pShader->FXSetPSFloat(paramPuddleName, &vPuddleParams, 1);

		if (rainVolParams.m_bApplyOcclusion && (CRenderer::CV_r_rain == 2))
		{
			static CCryNameR param3Name("g_RainOcc_TransMat");
			pShader->FXSetPSFloat(param3Name, (Vec4 *)rainVolParams.m_matOccTransRender.GetData(), 3);
		}

		short sX, sY, sWidth, sHeight;
		CDeferredShading::Instance().GetScissors(rainVolParams.m_vWorldPos, rainVolParams.m_fRadius, sX, sY, sWidth, sHeight);
		gcpRendD3D->EF_Scissor(true, sX , sY, sWidth, sHeight);
		if (gcpRendD3D->m_bDeviceSupports_NVDBT && fMaxZ > 0)
			SetDepthBoundTest(max(vDepthBounds.x, fMinZ), min(vDepthBounds.z, fMaxZ), true);















		FX_Commit();

		SD3DPostEffectsUtils::DrawFullScreenQuadWPOS(pSceneTarget->GetWidth(), pSceneTarget->GetHeight(), fMinZ);

    SD3DPostEffectsUtils::ShEndPass();




	}
	PROFILE_LABEL_POP("COMPOSITE_PASS");

  EF_Scissor(false, 0,0,0,0);
  SetDepthBoundTest(0.0f, 1.0f, false);

  gcpRendD3D->m_RP.m_FlagsShader_RT = nFlagsShaderRTSave;

  FX_ResetPipe();

  m_RP.m_PersFlags2 |= RBPF2_OCEANPARTICLES;

  if (m_LogFile)
    Logv(SRendItem::m_RecurseLevel[m_RP.m_nProcessThreadID], " +++ Deferred rain pass end +++ \n");

  SAFE_DELETE(pTexScreenSpaceRefl);

  return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool CD3D9Renderer::FX_DeferredRain()
{
	const N3DEngineCommon::SRainInfo & rainInfo = m_p3DEngineCommon.m_RainInfo;
	CEffectParam* pParam = PostEffectMgr()->GetByName("SceneRain_Active");
	if (pParam == 0 || pParam->GetParam() < 0.5f
		|| rainInfo.m_fAmount < 0.05f
		|| rainInfo.m_fRadius < 0.05f)
		return false;

	PROFILE_LABEL_PUSH("DEFERRED_RAIN");

	bool ret = true;
	if (rainInfo.m_fReflectionAmount > 0.05f)
		ret = FX_DeferredRainLayer();

	PROFILE_LABEL_POP("DEFERRED_RAIN");

	if (!ret)
		PostEffectMgr()->GetByName("SceneRain_Active")->SetParam(0);

	return ret;
}


bool CD3D9Renderer::FX_DeferredSnowLayer()
{
	const N3DEngineCommon::SSnowInfo & snowVolParams = m_p3DEngineCommon.m_SnowInfo;
	const N3DEngineCommon::SRainInfo & rainVolParams = m_p3DEngineCommon.m_RainInfo;
	CShader *pShader = CShaderMan::m_ShaderDeferredSnow;

	if ((CRenderer::CV_r_snow < 1) || (snowVolParams.m_fSnowAmount < 0.05f && snowVolParams.m_fFrostAmount < 0.05f && snowVolParams.m_fSurfaceFreezing < 0.05f) || snowVolParams.m_fRadius < 0.05f)
		return false;

	GPU_TIMER_START("Deferred Snow");
	PROFILE_LABEL_PUSH( "DEFERRED_SNOW" );

	uint64 nFlagsShaderRTSave = m_RP.m_FlagsShader_RT;

#if !defined(XENON) && !defined(PS3) && !defined(CAFE)
	FX_ApplyShaderQuality(eST_PostProcess);
#endif

	if (m_LogFile)
		Logv(SRendItem::m_RecurseLevel[m_RP.m_nProcessThreadID], " +++ Deferred snow pass begin +++ \n");

	PROFILE_LABEL_PUSH("COMPOSITE_PASS");
	{
		const CRenderCamera & cCam = gcpRendD3D->m_RP.m_TI[m_RP.m_nProcessThreadID].m_rcam;
		const Vec4 vDepthBounds = CDeferredShading::Instance().GetLightDepthBounds(snowVolParams.m_vWorldPos, snowVolParams.m_fRadius);
		const float fMinZ = CV_r_rain_ignore_nearest ? CV_r_DrawNearZRange : 0.f;
		float fMaxZ = -1.f;
		if (CV_r_rain_maxviewdist_deferred > cCam.Near)
			fMaxZ = (cCam.Far - (cCam.Near * cCam.Far) / CV_r_rain_maxviewdist_deferred) / (cCam.Far - cCam.Near);







































		gRenDev->m_cEF.mfRefreshSystemShader("DeferredSnow", CShaderMan::m_ShaderDeferredSnow);
		
		// Glossiness of normals has packed dynamic object mask.
		// We need to decode for areas that don't have snow otherwise we get the wrong glossiness values.
		// Also acts as a stretch-rect to the CTexture::s_ptexBackBuffer (used when rendering).
		{
			static CCryNameTSCRC pTechNameDecode = "NormalsGlossinessDecode";
			SD3DPostEffectsUtils::ShBeginPass(pShader, pTechNameDecode, FEF_DONTSETSTATES | FEF_DONTSETTEXTURES);
			FX_PushRenderTarget(0, CTexture::s_ptexBackBuffer, NULL);
			FX_SetState(GS_NODEPTHTEST);      
			PostProcessUtils().SetTexture(CTexture::s_ptexSceneNormalsMap, 0, FILTER_POINT);
			SD3DPostEffectsUtils::DrawFullScreenQuad(CTexture::s_ptexSceneNormalsMap->GetWidth(), CTexture::s_ptexSceneNormalsMap->GetHeight());
			SD3DPostEffectsUtils::ShEndPass();
			FX_PopRenderTarget(0);
		}

		FX_PushRenderTarget(0, CTexture::s_ptexSceneTarget, NULL);

		const ColorF clearColor = ColorF(0.0f,0.0f,0.0f,1.0f);
		EF_ClearBuffers(FRT_CLEAR_COLOR, &clearColor);

		FX_Commit();

		FX_PushRenderTarget(1, CTexture::s_ptexBackBuffer, NULL);

		m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]|g_HWSR_MaskBit[HWSR_SAMPLE3];
		if (IsHDRModeEnabled())
			m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_HDR_MODE];
		if (rainVolParams.m_bApplyOcclusion && (CRenderer::CV_r_snow == 2))
			m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];

		static CCryNameTSCRC pTechName = "Snow";
		SD3DPostEffectsUtils::ShBeginPass(pShader, pTechName, FEF_DONTSETSTATES);
		FX_SetState(GS_NODEPTHTEST);

		static CCryNameR paramName("g_SnowVolumeParams");
		const Vec3 & vCamPos = cCam.Orig;
		const Vec4 vSnowPosCS = Vec4(snowVolParams.m_vWorldPos, 1.f / max(snowVolParams.m_fRadius, 1e-3f));
		pShader->FXSetPSFloat(paramName, &vSnowPosCS, 1);

		static CCryNameR param1Name("g_SnowMultipliers");
		float fSnowAmount = snowVolParams.m_fSnowAmount;
		float fFrostAmount = snowVolParams.m_fFrostAmount;
		float fSurfaceFreezing = snowVolParams.m_fSurfaceFreezing;
		Vec4 vSnowMultipliers(fSnowAmount, fFrostAmount, fSurfaceFreezing, 0);
		pShader->FXSetPSFloat(param1Name, &vSnowMultipliers, 1);

		static CCryNameR param2Name("g_WorldViewPos");
		Vec4 pCamPosParam = Vec4(vCamPos, 1);
		pShader->FXSetPSFloat(param2Name, &pCamPosParam, 1);
		
		// Global wind params	
		Vec3 windVec = gEnv->p3DEngine->GetGlobalWind(false);
		if (rainVolParams.m_bApplyOcclusion)
		{
			static CCryNameR param3Name("g_SnowOcc_TransMat");
			pShader->FXSetPSFloat(param3Name, (Vec4 *)rainVolParams.m_matOccTransRender.GetData(), 3);

			// Pre-calculate wind-driven occlusion sample offset
			const float windOffsetScale = 15.f / (float)RAIN_OCC_MAP_SIZE;
			Vec3 windVecOcc = rainVolParams.m_matOccTransRender.TransformVector(windVec);
			windVecOcc.x *= windOffsetScale;
			windVecOcc.y *= windOffsetScale;

			static CCryNameR param4Name("g_SnowOcc_WindOffs");
			Vec4 pWindParamsOcc(windVecOcc.x, windVecOcc.y, 0, 0);
			pShader->FXSetPSFloat(param4Name, &pWindParamsOcc, 1);
		}

		static CCryNameR param4Name("g_WindDirection");
		Vec4 pWindParams(windVec.x, windVec.y, windVec.z, 1);
		pShader->FXSetPSFloat(param4Name, &pWindParams, 1);
		
		short sX, sY, sWidth, sHeight;
		CDeferredShading::Instance().GetScissors(snowVolParams.m_vWorldPos, snowVolParams.m_fRadius, sX, sY, sWidth, sHeight);
		gcpRendD3D->EF_Scissor(true, sX , sY, sWidth, sHeight);
		if (gcpRendD3D->m_bDeviceSupports_NVDBT && fMaxZ > 0)
			SetDepthBoundTest(max(vDepthBounds.x, fMinZ), min(vDepthBounds.z, fMaxZ), true);














	
		FX_Commit();
		
		SD3DPostEffectsUtils::DrawFullScreenQuadWPOS(CTexture::s_ptexSceneTarget->GetWidth(), CTexture::s_ptexSceneTarget->GetHeight(), fMinZ);

		SD3DPostEffectsUtils::ShEndPass();

		FX_PopRenderTarget(0);
		FX_PopRenderTarget(1);
				



	}
	PROFILE_LABEL_POP("COMPOSITE_PASS");

	EF_Scissor(false, 0,0,0,0);
	SetDepthBoundTest(0.0f, 1.0f, false);

	FX_Commit();

	PostProcessUtils().StretchRect(CTexture::s_ptexBackBuffer, CTexture::s_ptexSceneNormalsMap);
	
	gcpRendD3D->m_RP.m_FlagsShader_RT = nFlagsShaderRTSave;

	FX_ResetPipe();

	if (m_LogFile)
		Logv(SRendItem::m_RecurseLevel[m_RP.m_nProcessThreadID], " +++ Deferred snow pass end +++ \n");
	
	PROFILE_LABEL_POP( "DEFERRED_SNOW" );
	GPU_TIMER_STOP("Deferred Snow");

	return true;
}

bool CD3D9Renderer::FX_DeferredSnowDisplacement()
{
	const N3DEngineCommon::SSnowInfo & snowVolParams = m_p3DEngineCommon.m_SnowInfo;
	const N3DEngineCommon::SRainInfo & rainVolParams = m_p3DEngineCommon.m_RainInfo;
	CShader *pShader = CShaderMan::m_ShaderDeferredSnow;

	bool bQualityCheck = gRenDev->m_pPostProcessMgr && gRenDev->m_pPostProcessMgr->CheckPostProcessQuality( eRQ_VeryHigh, eSQ_VeryHigh );
	if( !bQualityCheck )
		return false;

	if ((CRenderer::CV_r_snow < 1 || CRenderer::CV_r_snow_displacement < 1) || snowVolParams.m_fSnowAmount < 0.05f || snowVolParams.m_fRadius < 0.05f)
		return false;

	GPU_TIMER_START("Deferred Snow Displacement");
	PROFILE_LABEL_PUSH( "DEFERRED_SNOW_DISPLACEMENT" );
	
	if (m_LogFile)
		Logv(SRendItem::m_RecurseLevel[m_RP.m_nProcessThreadID], " +++ Deferred snow displacement pass begin +++ \n");

	PROFILE_LABEL_PUSH("SNOW_DISPLACEMENT");

	static CCryNameR param5Name("g_CameraMatrix");
	Matrix44A matView;
	matView = m_RP.m_TI[m_RP.m_nProcessThreadID].m_cam.GetViewMatrix();

	// Adjust the camera matrix so that the camera space will be: +y = down, +z - towards, +x - right
	Vec3 zAxis = matView.GetRow(1);
	matView.SetRow(1, -matView.GetRow(2));
	matView.SetRow(2, zAxis);
	float z = matView.m13;
	matView.m13 = -matView.m23;
	matView.m23 = z;

	short sX, sY, sWidth, sHeight;
	CDeferredShading::Instance().GetScissors(snowVolParams.m_vWorldPos, snowVolParams.m_fRadius, sX, sY, sWidth, sHeight);
	EF_Scissor(true, sX , sY, sWidth, sHeight);

	{
		PROFILE_LABEL_PUSH( "GENERATE_HEIGHT_MAP" );
		static CCryNameTSCRC pTechNamePrepass = "ParallaxMapPrepass";
		SD3DPostEffectsUtils::ShBeginPass(pShader, pTechNamePrepass, FEF_DONTSETSTATES);

		FX_PushRenderTarget(0, CTexture::s_ptexBackBuffer, NULL);

		FX_SetState(GS_NODEPTHTEST);      

		pShader->FXSetPSFloat(param5Name, (Vec4*)matView.GetData(), 3);

		PostProcessUtils().SetTexture(CTexture::s_ptexSceneDiffuseAccMap, 0, FILTER_POINT);

		SD3DPostEffectsUtils::DrawFullScreenQuad(CTexture::s_ptexSceneNormalsMap->GetWidth(), CTexture::s_ptexSceneNormalsMap->GetHeight());

		SD3DPostEffectsUtils::ShEndPass();

		FX_PopRenderTarget(0);
		PROFILE_LABEL_POP( "GENERATE_HEIGHT_MAP" );
	}
	
	{
		static CCryNameTSCRC pTechNameMin = "ParallaxMapMin";
		SD3DPostEffectsUtils::ShBeginPass(pShader, pTechNameMin, FEF_DONTSETSTATES | FEF_DONTSETTEXTURES);

		FX_PushRenderTarget(0, CTexture::s_ptexSceneDiffuseAccMap, NULL);

		FX_SetState(GS_NODEPTHTEST);      
		PostProcessUtils().SetTexture(CTexture::s_ptexBackBuffer, 0, FILTER_POINT);

		SD3DPostEffectsUtils::DrawFullScreenQuad(CTexture::s_ptexSceneNormalsMap->GetWidth(), CTexture::s_ptexSceneNormalsMap->GetHeight());

		SD3DPostEffectsUtils::ShEndPass();

		FX_PopRenderTarget(0);
	}

	// Copy screen to texture for displacement.
	FX_ScreenStretchRect(CTexture::s_ptexHDRTarget);

	// Iteratively apply displacement to maximize quality and minimize sample count.
	{
		PROFILE_LABEL_PUSH( "APPLY_DISPLACEMENT" );
		static CCryNameTSCRC pTechNameApply = "ParallaxMapApply";
		static CCryNameR pPassParamsName("g_DisplacementParams");
		Vec4 pPassParams(0, 0, 0, 0);

		uint64 nFlagsShaderRTSave = m_RP.m_FlagsShader_RT;		
		m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE0];

		// First pass.
		FX_PushRenderTarget(0, CTexture::s_ptexSceneTarget, NULL);
		FX_PushRenderTarget(1, CTexture::s_ptexSceneSpecularAccMap, NULL);
					
		SD3DPostEffectsUtils::ShBeginPass(pShader, pTechNameApply, FEF_DONTSETSTATES);
		FX_SetState(GS_NODEPTHTEST);      

		PostProcessUtils().SetTexture(CTexture::s_ptexHDRTarget, 0, FILTER_LINEAR);
		PostProcessUtils().SetTexture(CTexture::s_ptexSceneDiffuseAccMap, 1, FILTER_LINEAR);
		pPassParams.x = 1.0f;
		pShader->FXSetPSFloat(pPassParamsName, &pPassParams, 1);

		SD3DPostEffectsUtils::DrawFullScreenQuad(CTexture::s_ptexSceneTarget->GetWidth(), CTexture::s_ptexSceneTarget->GetHeight());
		SD3DPostEffectsUtils::ShEndPass();

		FX_PopRenderTarget(0);
		FX_PopRenderTarget(1);
		
		// Second pass.
		FX_PushRenderTarget(0, CTexture::s_ptexHDRTarget, NULL);
		FX_PushRenderTarget(1, CTexture::s_ptexSceneDiffuseAccMap, NULL);
		
		SD3DPostEffectsUtils::ShBeginPass(pShader, pTechNameApply, FEF_DONTSETSTATES);
		FX_SetState(GS_NODEPTHTEST);      

		PostProcessUtils().SetTexture(CTexture::s_ptexSceneTarget, 0, FILTER_LINEAR);
		PostProcessUtils().SetTexture(CTexture::s_ptexSceneSpecularAccMap, 1, FILTER_LINEAR);
		pPassParams.x = 0.5f;
		pShader->FXSetPSFloat(pPassParamsName, &pPassParams, 1);

		SD3DPostEffectsUtils::DrawFullScreenQuad(CTexture::s_ptexSceneTarget->GetWidth(), CTexture::s_ptexSceneTarget->GetHeight());
		SD3DPostEffectsUtils::ShEndPass();

		FX_PopRenderTarget(0);
		FX_PopRenderTarget(1);

		// Third pass.
		m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];

		FX_PushRenderTarget(0, CTexture::s_ptexSceneTarget, NULL);
		FX_PushRenderTarget(1, CTexture::s_ptexZTarget, NULL);

		SD3DPostEffectsUtils::ShBeginPass(pShader, pTechNameApply, FEF_DONTSETSTATES);
		FX_SetState(GS_NODEPTHTEST);      

		PostProcessUtils().SetTexture(CTexture::s_ptexHDRTarget, 0, FILTER_LINEAR);
		PostProcessUtils().SetTexture(CTexture::s_ptexSceneDiffuseAccMap, 1, FILTER_LINEAR);
		pPassParams.x = 0.25f;
		pShader->FXSetPSFloat(pPassParamsName, &pPassParams, 1);

		SD3DPostEffectsUtils::DrawFullScreenQuad(CTexture::s_ptexSceneTarget->GetWidth(), CTexture::s_ptexSceneTarget->GetHeight());
		SD3DPostEffectsUtils::ShEndPass();

		FX_PopRenderTarget(0);
		FX_PopRenderTarget(1);

		gcpRendD3D->m_RP.m_FlagsShader_RT = nFlagsShaderRTSave;

		PROFILE_LABEL_POP( "APPLY_DISPLACEMENT" );
	}

	PostProcessUtils().CopyTextureToScreen(CTexture::s_ptexSceneTarget);

	PROFILE_LABEL_POP("SNOW_DISPLACEMENT");

	EF_Scissor(false, 0,0,0,0);

	FX_Commit();

	FX_ResetPipe();

	if (m_LogFile)
		Logv(SRendItem::m_RecurseLevel[m_RP.m_nProcessThreadID], " +++ Deferred snow displacement pass end +++ \n");

	PROFILE_LABEL_POP( "DEFERRED_SNOW_DISPLACEMENT" );
	GPU_TIMER_STOP("Deferred Snow Displacement");

	return true;
}